import math
import re
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import datetime, timedelta
import json
import os
import smtplib
from docx import Document
from docx.shared import RGBColor
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.enum.style import WD_STYLE_TYPE
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
from docx.shared import Pt  # 导入 Pt 类
from ConfigManager import ConfigManager
from DocxStyleHandler import DocxStyleHandler
from JsonUtil import JsonUtil
from LLMProcess import LLMProcess
import Logger_config

# 设置日志配置
logger = Logger_config.setup_logger()

class WordProcessor:
    def __init__(self):        
        today = datetime.now()
        # 计算上周一、上周五的日期
        start_of_week = today - timedelta(days=today.weekday()+7)
        friday_of_week = start_of_week + timedelta(days=4)

        # 格式化日期为YYYYMMDD
        self.start_of_week_formatted = start_of_week.strftime('%Y年%m月%d日')
        self.friday_of_week_formatted = friday_of_week.strftime('%Y年%m月%d日')
        self.week_number_monday_start = today.strftime("%W")  # 周一作为一周的开始

        self.cm = ConfigManager()
        
    def check_document_integrity(self,old_file_path,new_file_path):
        """
        检查文档内容的完整性，并根据LLM模型的响应判断文档是否符合要求。
        
        :return: 无返回值，但会通过日志记录文档是否符合要求
        
        逻辑流程：
        1. 调用 `read_word_document` 方法读取文档内容。
        2. 构造LLM提示信息，包含系统角色和用户角色的内容。
        3. 调用 `LLMProcess.getLLMTextResponse` 方法获取LLM模型的响应。
        4. 根据LLM模型的响应内容判断文档是否符合要求，并记录日志。
        """
        check_result,red_content = self.check_red_font(new_file_path)
        model_base_url = self.cm.get_merged_config()['model']['base_url']  # 模型的基础URL
        model_api_key = self.cm.get_merged_config()['model']['api_key']  # 模型的API密钥
        model_name = self.cm.get_merged_config()['model']['model_name']  # 模型名称
        model_max_tokens = self.cm.get_merged_config()['model']['max_tokens']  # 设置模型生成的最大token数，控制生成内容的长度
        model_temperature = self.cm.get_merged_config()['model']['temperature']  # 设置模型的温度参数，控制生成内容的随机性，值越高生成的内容越随机
        model_stream = self.cm.get_merged_config()['model']['stream']  # 设置是否以流式方式获取模型生成的内容，True表示逐步返回生成结果，False表示一次性返回

        if check_result:
            logger.info(f"文档{new_file_path}存在红色字体的情况，详情如下：\n{red_content}")
            return f"文档内容完整性检查未通过，原因：文档存在红色字体的情况，详情如下：\n{red_content}" 

        old_text = self.read_word_document(old_file_path)
        new_text = self.read_word_document(new_file_path)
        logger.info(f"新旧文件内容读取完毕")
        
        systemprompt = f"""你是一个文案分析大师，请你根据用户提供的两段新旧文本判断新文本内容是否已更新。 
        1、大段内容与旧文本保持一致（重复度超过20%）则判定未更新，提示当前重复度数值，且返回全部重复的内容；
        2、【上周日期】：周一：{self.start_of_week_formatted} - 周五：{self.friday_of_week_formatted}。【新文本】中出现【上周日期】以外的日期则判定为未更新，返回不符合要求原因；
        最后请总结概括检查结果并返回。
        """

        userprompt = f"【旧文本】：\n{old_text}\n【新文本】：\n{new_text}"
        prompts = [
                    {"role": "system", "content": systemprompt},
                    {"role": "user", "content": userprompt}
                ]
        logger.info("开始调用大模型检查文档内容是否完整")
        result = LLMProcess.getLLMTextResponse(model_name,model_api_key,model_base_url,model_max_tokens,model_temperature,model_stream,prompts)
        logger.info(f"检查结果：{result}")
        return f"{result}"
    
    def generate_format_doc(self,file_path):
        raw_path = self.cm.get_merged_config()["output"]["file_path"]
        output_file_path = os.path.join(os.path.expandvars(raw_path),f"产业数智化产品部部门简讯（第{self.week_number_monday_start}周，{self.start_of_week_formatted}-{self.friday_of_week_formatted}）.docx")
        model_base_url = self.cm.get_merged_config()['model']['base_url']  # 模型的基础URL
        model_api_key = self.cm.get_merged_config()['model']['api_key']  # 模型的API密钥
        model_name = self.cm.get_merged_config()['model']['model_name']  # 模型名称
        model_max_tokens = self.cm.get_merged_config()['model']['max_tokens']  # 设置模型生成的最大token数，控制生成内容的长度
        model_temperature = self.cm.get_merged_config()['model']['temperature']  # 设置模型的温度参数，控制生成内容的随机性，值越高生成的内容越随机
        model_stream = self.cm.get_merged_config()['model']['stream']  # 设置是否以流式方式获取模型生成的内容，True表示逐步返回生成结果，False表示一次性返回
        # 确保目录存在
        os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
        # 读取输入文档内容
        doc_texts = self.read_word_document_format(file_path,True)

    def generate_optimized_document(self,file_path):
        raw_path = self.cm.get_merged_config()["output"]["file_path"]
        output_file_path = os.path.join(os.path.expandvars(raw_path),f"产业数智化产品部部门简讯（第{self.week_number_monday_start}周，{self.start_of_week_formatted}-{self.friday_of_week_formatted}）.docx")
        model_base_url = self.cm.get_merged_config()['model']['base_url']  # 模型的基础URL
        model_api_key = self.cm.get_merged_config()['model']['api_key']  # 模型的API密钥
        model_name = self.cm.get_merged_config()['model']['model_name']  # 模型名称
        model_max_tokens = self.cm.get_merged_config()['model']['max_tokens']  # 设置模型生成的最大token数，控制生成内容的长度
        model_temperature = self.cm.get_merged_config()['model']['temperature']  # 设置模型的温度参数，控制生成内容的随机性，值越高生成的内容越随机
        model_stream = self.cm.get_merged_config()['model']['stream']  # 设置是否以流式方式获取模型生成的内容，True表示逐步返回生成结果，False表示一次性返回
        # 确保目录存在
        os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
        """
        读取Word文件并根据提示词优化，然后保存到新的Word文档中。
        """
        # 读取输入文档内容
        doc_texts = self.read_word_document_format(file_path,True)

        systemprompt = """你是一个文案优化大师， 不仅能优化文案内容，也能优化文案格式。
        用户会提供包含文案内容的【文档内容】给你，请完成以下要求：
        1、删除@xxx，xxx或空白的内容；删除（填写人：xxx）之后的整行内容，如：（填写人：李泽丽、刘学巍）（更新后的内容请标黑）；删除正文空白的标题行；
        2、修改明显的错别字和病句；
        3、不要随意删除括号和序号，不要随意对内容做删减和修改；
        4、不要随意改变缩进格式；
        5、输出必须是一个有效的JSON数组，必须以[开头，以]结尾，仅保留纯JSON内容，不要添加任何标题、注释或其他非JSON内容。正确实例如下：
        [{"r": [{"t": "我部许寒旭总参加平台组联席会", "fs": 12, "fn": "仿宋_GB2312", "fb": false}], "a": 2, "s": "Heading 1"}]
        6、所有符号必须使用英文半角（如冒号、引号、括号、逗号等）；
        7、t:文本内容、fs:字体大小、fn:字体名称三个字段必须包含在输出JSON中；fb:粗体，如果为false或null则不要输出；a:对齐方式，如果为0或null则不要输出；s:样式，为Normal或null则不要输出；
        8、在处理文档时，请识别不同级别的标题和正文，并按如下要求修改字体、字号和样式。输出必须遵循：
           - 一级标题：使用"黑体"字体，字号为16pt，必须使用"Heading 1样式"，不能使用"title"样式；
           - 二级标题：使用"楷体_GB2312"字体，字号为14pt，使用粗体(fb: true)；
           - 三级标题：使用"仿宋_GB2312"字体，字号为14pt，使用粗体(fb: true)；
           - 正文：使用"仿宋_GB2312"字体，字号为12pt，使用"Normal"样式；
           不能出现上述要求以外的字体。
        """
        addtional_content = f"9、文档内容最开头的一级标题中出现的周数、日期请使用如下数据替换：{self.week_number_monday_start}、{self.start_of_week_formatted}、{self.friday_of_week_formatted}"
        systemprompt += addtional_content

        optimized_contents = []

        for doc_text in doc_texts:
            userprompt = f"【文档内容】：\n{doc_text}"     
            # 准备LLM提示
            prompts = [
                {"role": "system", "content": systemprompt},
                {"role": "user", "content": userprompt}
            ]

            logger.info("开始调用大模型进行文案优化")
            # 调用LLM生成摘要
            llm_result = LLMProcess.getLLMTextResponse(
                model_name,
                model_api_key,
                model_base_url,
                model_max_tokens,
                model_temperature,
                model_stream,
                prompts
            )
            
            summary_content = JsonUtil.reshape(llm_result)#重新整理JSON格式

            try:             
                formatted_content = json.loads(summary_content)
            except json.JSONDecodeError as e:
                logger.error(f"JSON字符串不符合JSON标准。错误信息: {e}")
                logger.error(f"原始内容: {summary_content}")
                return False,f"JSON字符串不符合JSON标准。错误信息: {e}"
                        
            optimized_contents.append(formatted_content)

        # 合并所有优化后的内容
        final_optimized_content = []
        for content in optimized_contents:
            final_optimized_content.extend(content)

        # 生成摘要文档
        self.generate_word_document_format(output_file_path, final_optimized_content)
        logger.info(f"优化后文档已成功保存到： {output_file_path}")
        return True,f"优化后文档已成功保存到： {output_file_path}"

    def generate_summary(self,file_path):
        raw_path = self.cm.get_merged_config()["output"]["file_path"]
        output_file_path = os.path.join(os.path.expandvars(raw_path),f"产业数智化产品部部门简讯（第{self.week_number_monday_start}周，{self.start_of_week_formatted}-{self.friday_of_week_formatted}）.docx")
        model_base_url = self.cm.get_merged_config()['model']['base_url']  # 模型的基础URL
        model_api_key = self.cm.get_merged_config()['model']['api_key']  # 模型的API密钥
        model_name = self.cm.get_merged_config()['model']['model_name']  # 模型名称
        model_max_tokens = self.cm.get_merged_config()['model']['max_tokens']  # 设置模型生成的最大token数，控制生成内容的长度
        model_temperature = self.cm.get_merged_config()['model']['temperature']  # 设置模型的温度参数，控制生成内容的随机性，值越高生成的内容越随机
        model_stream = self.cm.get_merged_config()['model']['stream']  # 设置是否以流式方式获取模型生成的内容，True表示逐步返回生成结果，False表示一次性返回
        # 确保目录存在
        os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
    
        # 读取优化后文档内容
        optimized_text = self.read_word_document_format(file_path,False)
        systemprompt = """你是一个文案总结大师， 
        用户会提供包含内容的【文档内容】给你，请完成以下要求：
        1、请重点摘录含关键词：集团、领导、王建中、建中、黄刚、黄总、乔建设、乔总、刘庆华、庆华、周威、许寒旭、潘永高、马兵、黄承伟、庄凯凯、郭广跃、高博、程元森、陈飞、姚宇华、政府、展会、展厅、大会、会议、论坛的段落，并按照关键词先后顺序进行排序；        
        2、输出必须是一个有效的JSON数组，必须以[开头，以]结尾，仅保留纯JSON内容，不要添加任何标题、注释或其他非JSON内容。正确实例如下：
        [{"r": [{"t": "我部许寒旭总参加平台组联席会", "fs": 12, "fn": "仿宋_GB2312", "fb": false}], "a": 2, "s": "Heading 1"}]
        3、所有符号必须使用英文半角（如冒号、引号、括号、逗号等）；
        4、t:文本内容、fs:字体大小、fn:字体名称三个字段必须包含在输出JSON中；fb:粗体，如果为false或null则不要输出；a:对齐方式，如果为0或null则不要输出；s:样式，为Normal或null则不要输出；
        5、在处理文档时，请识别不同级别的标题和正文，并按如下要求修改字体、字号和样式。输出必须遵循：
           - 一级标题：使用"黑体"字体，字号为16pt，必须使用"Heading 1样式"，不能使用"title"样式；
           - 二级标题：使用"楷体_GB2312"字体，字号为14pt，使用粗体(fb: true)；
           - 三级标题：使用"仿宋_GB2312"字体，字号为14pt，使用粗体(fb: true)；
           - 正文：使用"仿宋_GB2312"字体，字号为12pt，使用"Normal"样式；
           不能出现上述要求以外的字体。
        """
        addtional_content = f"6、在文档内容最开头，使用'仿宋_GB2312'字体，字号为12pt，'Normal'样式，插入如下内容：为增进各网格对部门重要工作和重大活动的了解，现将{self.start_of_week_formatted}至{self.friday_of_week_formatted}部门简讯通告如下："
        systemprompt = systemprompt + addtional_content

        userprompt = f"【文档内容】：\n{optimized_text}"     
        # 准备LLM提示
        prompts = [
            {"role": "system", "content": systemprompt},
            {"role": "user", "content": userprompt}
        ]
        logger.info("开始调用大模型进行总结优化")
        # 调用LLM生成摘要
        llm_result = LLMProcess.getLLMTextResponse(
            model_name,
            model_api_key,
            model_base_url,
            model_max_tokens,
            model_temperature,
            model_stream,
            prompts
        )

        summary_content = JsonUtil.reshape(llm_result)#重新整理JSON格式
        
        try:             
            formatted_content = json.loads(summary_content)            
            all_content = formatted_content + optimized_text
        except json.JSONDecodeError as e:
            logger.error(f"JSON字符串不符合JSON标准。错误信息: {e}")
            logger.error(f"原始内容: {summary_content}")
            return False,f"JSON字符串不符合JSON标准。错误信息: {e}"
        
        # 生成摘要文档
        self.generate_word_document_format(output_file_path, all_content)
        logger.info(f"摘要+优化后文档已成功保存到 {output_file_path}")
        return True,f"摘要+优化后文档已成功保存到 {output_file_path}"
    def read_word_document_format(self, file_path, split=True):
        """
        读取Word文档的内容及其格式。

        :param file_path: Word文档的路径
        :param split: 是否按12000字符长度分段处理，默认为True
        :return: 文档的文本内容及其格式，可能包含多个部分
        """
        os.chdir(os.path.dirname(os.path.abspath(__file__)))  # 将工作目录锁定为脚本所在目录，防止使用相对路径时找不到文件
        try:
            # 检查文件是否存在
            if not os.path.exists(file_path):
                raise FileNotFoundError(f"文件 {file_path} 不存在。")
            
            # 检查文件是否为 .docx 文件
            if not file_path.endswith('.docx'):
                raise ValueError(f"文件 {file_path} 不是有效的 .docx 文件。")
                        
            # 打开并读取文档内容
            doc = Document(file_path)
            full_content = []
            current_content = []

            for para in doc.paragraphs:
                para_content = {
                    'r': []
                }
                if para.alignment:                    
                    if para.alignment != 0:
                        para_content['a'] = para.alignment
                if para.style.name:
                    style_name = para.style.name.lower()
                    if style_name != "normal":
                        para_content['s'] = style_name

                for run in para.runs:
                    text = run.text
                    font = run.font
                    font_size = font.size.pt if font.size else None
                    bold = font.bold
                    font_name = font.name

                    # 格式化信息
                    if text.strip():
                        run_info = {'t': text}
                        if font_size:
                            run_info['fs'] = int(math.floor(font_size))#为了减少json体积，将浮点数转换为整数（向下取整）
                        if font_name:
                            run_info['fn'] = font_name
                        if bold:
                            run_info['fb'] = bold
                        para_content['r'].append(run_info)

                if para_content['r']:  # 只有当 'r' 中有内容时才 append
                    current_content.append(para_content)

                # 如果启用分段且当前内容超过12k字符，则分割
                if split:
                    current_text = json.dumps(current_content, ensure_ascii=False)
                    if len(current_text) > 10000:
                        full_content.append(current_content)
                        current_content = []
                        logger.info(f"当前内容字符长度：{len(current_text)}")

           # 添加剩余内容
            if current_content:
                if split:
                    full_content.append(current_content)
                    logger.info(f"当前内容字符长度：{len(json.dumps(current_content, ensure_ascii=False))}")
                else:
                    # split=False 时，将 current_content 直接合并到 full_content 中（即扁平化）
                    full_content.extend(current_content)

            logger.info(f"最终返回结果：{full_content}")
            return full_content
        except FileNotFoundError as fnf_err:
            logger.error(fnf_err)
            raise
        except ValueError as ve:
            logger.error(ve)
            raise
        except Exception as e:
            logger.error(f"读取文档时发生错误: {e}")
            raise
    def read_word_document(self, file_path):
        """
        只读取Word文档的内容。

        :param file_path: Word文档的路径
        :return: 文档的文本内容
        """
        os.chdir(os.path.dirname(os.path.abspath(__file__)))  # 将工作目录锁定为脚本所在目录，防止使用相对路径时找不到文件
        try:
            # 检查文件是否存在
            if not os.path.exists(file_path):
                raise FileNotFoundError(f"文件 {file_path} 不存在。")
            
            # 检查文件是否为 .docx 文件
            if not file_path.endswith('.docx'):
                raise ValueError(f"文件 {file_path} 不是有效的 .docx 文件。")
                        
            # 打开并读取文档内容
            doc = Document(file_path)
            full_text = '\n'.join([paragraph.text for paragraph in doc.paragraphs])

            text_length = len(full_text)
            if text_length > 100000:
                logger.error(f"暂不支持超过100K字符的word文档！您的文档长度为{text_length}，您可以拆分为多个小文件处理。")
                raise ValueError(f"暂不支持超过100K字符的word文档！您的文档长度为{text_length}，您可以拆分为多个小文件处理。")

            return full_text
        except FileNotFoundError as fnf_err:
            logger.error(fnf_err)
            raise
        except ValueError as ve:
            logger.error(ve)
            raise
        except Exception as e:
            logger.error(f"读取文档时发生错误: {e}")
            raise

    def read_word_doc(self, file_path, max_chunk_length=6000):
        """
        按段落读取Word文档，并将段落按字符长度切分为多个块。
        每个块由完整段落组成，总字符数不超过 max_chunk_length。

        :param file_path: Word文档路径
        :param max_chunk_length: 每个块的最大字符长度，默认6000
        :return: List[str]，外层列表表示块，每个块是拼接后的字符串
        """
        try:
            if not os.path.exists(file_path):
                raise FileNotFoundError(f"文件 {file_path} 不存在。")
            if not file_path.endswith('.docx'):
                raise ValueError(f"文件 {file_path} 不是有效的 .docx 文件。")

            doc = Document(file_path)
            chunks = []
            current_chunk = []
            current_length = 0

            for para in doc.paragraphs:
                paragraph_text = para.text.strip()
                if not paragraph_text:
                    continue  # 跳过空段落

                para_len = len(paragraph_text)

                # 如果当前段落本身超过最大限制，单独作为一个 chunk
                if para_len >= max_chunk_length:
                    if current_chunk:
                        chunks.append('\n'.join(current_chunk))
                    chunks.append(paragraph_text)
                    current_chunk = []
                    current_length = 0
                    continue

                # 若加入当前段落后超出限制，则保存当前 chunk 并新建
                if current_length + para_len > max_chunk_length:
                    if current_chunk:
                        chunks.append('\n'.join(current_chunk))
                    current_chunk = [paragraph_text]
                    current_length = para_len
                else:
                    current_chunk.append(paragraph_text)
                    current_length += para_len

            # 添加最后剩余的段落
            if current_chunk:
                chunks.append('\n'.join(current_chunk))

            total_length = 0
            logger.info(f"共分割为 {len(chunks)} 块")
            for i, chunk in enumerate(chunks):
                total_length = total_length + len(chunk)
                logger.info(f"第{i+1}块字符数: {len(chunk)}")
                logger.info(f"第{i+1}块内容:\n{chunk}")

            logger.info(f"总字符数: {total_length}")
            return chunks

        except FileNotFoundError as fnf_err:
            logger.error(fnf_err)
            raise
        except ValueError as ve:
            logger.error(ve)
            raise
        except Exception as e:
            logger.error(f"读取文档时发生错误: {e}")
            raise
    def generate_word_document(self, file_path, content):
        """
        生成一个新的Word文档，并写入指定的文本内容。

        :param file_path: 新文档的保存路径
        :param content: 要写入文档的文本内容
        """
        try:
            # 如果文件存在，则加载现有文档；否则创建新文档
            if os.path.exists(file_path):
                os.remove(file_path)
                logger.info(f"已删除旧文件: {file_path}")

            # 创建新文档
            doc = Document()
            
            # 将内容添加到文档中
            doc.add_paragraph(content)
            
            # 保存文档
            doc.save(file_path)
            logger.info(f"写入文档内容是: {content}")
            logger.info(f"文档已成功保存到 {file_path}")
        except FileNotFoundError as fnf_err:
            logger.error(f"文件路径无效: {fnf_err}")
            raise
        except PermissionError as perm_err:
            logger.error(f"权限不足: {perm_err}")
            raise
        except Exception as e:
            logger.error(f"生成文档时发生错误: {e}")
            raise
    def generate_word_document_format(self, file_path, formatted_content):
        """
        生成一个新的Word文档，并写入指定的文本内容及其格式。

        :param file_path: 新文档的保存路径
        :param formatted_content: 要写入文档的格式化内容
        """
        try:
            # 如果文件存在，则加载现有文档；否则创建新文档
            if os.path.exists(file_path):
                os.remove(file_path)
                logger.info(f"已删除旧文件: {file_path}")

            logger.info(f"写入文档内容是content: {formatted_content}")

            # 创建新文档
            doc = Document()
            ALIGNMENT_MAP = {
                0: WD_PARAGRAPH_ALIGNMENT.LEFT,
                1: WD_PARAGRAPH_ALIGNMENT.CENTER,
                2: WD_PARAGRAPH_ALIGNMENT.RIGHT,
                3: WD_PARAGRAPH_ALIGNMENT.JUSTIFY
            }

            # 确保文档中存在默认样式
            default_style_name = 'Normal'
            if default_style_name not in doc.styles:
                # 如果默认样式不存在，尝试创建（通常不会执行到这里）
                normal_style = doc.styles.add_style(default_style_name, WD_STYLE_TYPE.PARAGRAPH)
            else:
                normal_style = doc.styles[default_style_name]
                
            # 获取内置样式映射（保持首字母大写）
            builtin_styles = {
                'heading 1': 'Heading 1',
                'heading 2': 'Heading 2',
                'heading 3': 'Heading 3',
                'normal': 'Normal'
            }
            for para_content in formatted_content:
                # 获取并标准化样式名称
                style_name = para_content.get('s', default_style_name).strip().lower()
                
                # 如果是内置样式，使用Word标准样式名称（保持首字母大写）
                if style_name in builtin_styles:
                    actual_style_name = builtin_styles[style_name]
                else:
                    actual_style_name = style_name
                    
                # 检查样式是否存在
                if actual_style_name not in [s.name for s in doc.styles]:
                    # 创建新样式，并基于Normal样式
                    new_style = doc.styles.add_style(actual_style_name, WD_STYLE_TYPE.PARAGRAPH)
                    new_style.base_style = normal_style
                
                alignment = ALIGNMENT_MAP.get(para_content.get('a'), WD_PARAGRAPH_ALIGNMENT.LEFT)
                para = doc.add_paragraph(style=actual_style_name)  # 使用实际存在的样式名称
                para.alignment = alignment

                for run_info in para_content['r']:
                    text = run_info.get('t')
                    if not text:
                        continue

                    run = para.add_run(text)
                    run.font.color.rgb = RGBColor(0, 0, 0)
                    font_size = run_info.get('fs')
                    if font_size is not None:
                        run.font.size = Pt(font_size)

                    # 检查并设置 bold 属性
                    font_bold = run_info.get('fb')
                    if font_bold is not None:
                        run.font.bold = font_bold
                    else:
                        run.font.bold = False
                     # 检查并设置 font_name 属性
                    font_name = run_info.get('fn')
                    if font_name is not None:
                        run.font.name = font_name
                        # 设置字体为中文字体时，需要设置字体的东アジア字体
                        r = run._element
                        rPr = r.get_or_add_rPr()
                        rFonts = OxmlElement('w:rFonts')
                        rFonts.set(qn('w:eastAsia'), font_name)
                        rPr.append(rFonts)            

            # 保存文档
            doc.save(file_path)
            logger.info(f"文档已成功保存到 {file_path}")
        except FileNotFoundError as fnf_err:
            logger.error(f"文件路径无效: {fnf_err}")
            raise
        except PermissionError as perm_err:
            logger.error(f"权限不足: {perm_err}")
            raise
        except Exception as e:
            logger.error(f"生成文档时发生错误: {e}")
            raise
    def check_red_font(self,file_path):
        """
        检查文档中是否存在红色字体的文本。

        参数:
        - file_path: str，要检查的Word文档的文件路径。

        返回:
        - bool，如果文档中存在红色字体的文本，则返回True；否则返回False。
        """
        # 获取当前脚本文件的绝对路径
        os.chdir(os.path.dirname(os.path.abspath(__file__)))  # 将工作目录锁定为脚本所在目录，防止使用相对路径时找不到文件
        doc = Document(file_path)
        is_red = False
        red_texts = []

        # 遍历所有段落
        for para in doc.paragraphs:
            for run in para.runs:
                if run.text.strip():  # 确保文本不为空
                    font_color = run.font.color.rgb
                    if self.is_visual_red(font_color):
                        logger.info(f"发现红色文本: '{run.text}'，颜色值: {font_color}")
                        is_red = True
                        red_texts.append(run.text)

        # 过滤掉空字符串和只包含空白字符的字符串
        red_texts = [text for text in red_texts if text.strip()]

        # 将红色文本内容合并为一个字符串
        red_texts_str = '\n'.join(red_texts)
        return is_red,red_texts_str
    
    def is_visual_red(self, rgb):
        if rgb is None:
            return False  # 如果 RGB 值为 None，则不是红色

        r, g, b = rgb

        # 红色的典型特征：R 值高，G 和 B 值低
        return r >= 128 and g <= 80 and b <= 80
    
    def send_email(self, subject, recipient, attachment_path):
        # 邮件服务器配置
        smtp_server = 'smtp.cmsr.chinamobile.com'  # 根据实际情况修改SMTP服务器地址
        smtp_port = 465  # 根据实际情况修改SMTP服务器端口
        smtp_user = 'chengyuansen@cmsr.chinamobile.com'  # 发件人邮箱地址
        smtp_password = 'your_email_password'  # 发件人邮箱密码

        # 创建邮件对象
        msg = MIMEMultipart()
        msg['From'] = smtp_user
        msg['To'] = recipient
        msg['Subject'] = subject

        # 添加邮件正文
        body = "请参阅附件中的产业数智化产品部部门简讯。"
        msg.attach(MIMEText(body, 'plain'))

        # 添加附件
        with open(attachment_path, "rb") as attachment:
            part = MIMEApplication(attachment.read(), Name=os.path.basename(attachment_path))
        part['Content-Disposition'] = f'attachment; filename="{os.path.basename(attachment_path)}"'
        msg.attach(part)

        # 连接到SMTP服务器并发送邮件
        try:
            with smtplib.SMTP(smtp_server, smtp_port) as server:
                server.starttls()  # 启用TLS加密
                server.login(smtp_user, smtp_password)
                server.sendmail(smtp_user, recipient, msg.as_string())
            logger.info("邮件发送成功。")
        except Exception as e:
            logger.info(f"邮件发送失败: {e}")
    
    def generate_styled_document(self, file_path,user_input_prompt):
        """
        读取Word文档内容，分块调用LLM生成带样式的文本，并保存到新文档中。

        :param file_path: 输入Word文档的路径
        :return: 无返回值，但会生成一个包含处理后内容的新Word文档
        """
        # 获取模型配置参数
        model_config = self.cm.get_merged_config()['model']
        model_base_url = model_config['base_url']
        model_api_key = model_config['api_key']
        model_name = model_config['model_name']
        model_max_tokens = model_config['max_tokens']
        model_temperature = model_config['temperature']
        model_stream = model_config['stream']

        word_style_schema = self.cm.get_merged_config()['word']['style']['schema']
        style_system_prompt = self.cm.get_merged_config()['system_prompt']['style']
        style_system_prompt = style_system_prompt.replace("{word_style_schema}",word_style_schema)

        raw_path = self.cm.get_merged_config()["output"]["file_path"]
        original_file_name = os.path.basename(file_path) #输入文件名
        file_root, file_ext = os.path.splitext(original_file_name)  # 分割文件名和扩展名

        # 获取当前时间，格式为 YYYYMMDDHHMMSS
        current_time = datetime.now().strftime("%Y%m%d%H%M%S")
        # 构造新的文件名
        new_file_name = f"{file_root}_{current_time}{file_ext}"
        # 构造输出文件路径
        output_file_path = os.path.join(os.path.expandvars(raw_path),new_file_name)

        # 一次性读取所有文本内容，用来生成样式库
        content = self.read_word_document(file_path)

        userprompt = f"""
            {user_input_prompt}
            【文档内容】：
            {content}
        """        
        prompts = [
            {"role": "system", "content": style_system_prompt},
            {"role": "user", "content": userprompt}
        ]

        # 调用大模型获取样式定义
        llm_result = LLMProcess.getLLMTextResponse(
            model_name, model_api_key, model_base_url,
            model_max_tokens, model_temperature, model_stream,
            prompts
        )
        logger.info(f"样式定义结果：{llm_result}")
        word_styles = JsonUtil.reshape(llm_result)

        try:
            formatted_word_styles = json.loads(word_styles)
        except json.JSONDecodeError as e:
            logger.error(f"JSON字符串不符合JSON标准。错误信息: {e}")
            logger.error(f"原始内容: {word_styles}")
            raise ValueError(f"JSON字符串不符合JSON标准。错误信息: {e}")

        dsh = DocxStyleHandler()
        doc = Document()
        dsh.create_or_update_styles(doc, formatted_word_styles)

        # 获取排版定义提示词
        layout_system_prompt = self.cm.get_merged_config()['system_prompt']['layout']
        
        all_results = []
        chunks = self.read_word_doc(file_path)  # 返回 List[str]

        for i, chunk in enumerate(chunks):
            if (i + 1) % 5 == 0:
                logger.info(f"正在处理第 {i+1} 个块...")

            if len(chunks) > 1:  # 多块内容时，添加块信息
                comment = f"""
                    【文档块信息】：
                    【文档内容】共切分为{len(chunks)}个块，当前是第{i + 1}块。
                """
            else:  # 单块内容时，不添加块信息
                comment = ""

            userprompt = f"""
                【样式库】：
                {word_styles}  
                {comment}              
                【文档内容】：
                {chunk}
            """

            prompts = [
                {"role": "system", "content": layout_system_prompt},
                {"role": "user", "content": userprompt}
            ]

            llm_result = LLMProcess.getLLMTextResponse(
                model_name, model_api_key, model_base_url,
                model_max_tokens, model_temperature, model_stream,
                prompts
            )
            applied_content = JsonUtil.reshape(llm_result)

            try:
                formatted_word_content = json.loads(applied_content)
                all_results.append(formatted_word_content)
            except json.JSONDecodeError as e:
                logger.error(f"JSON字符串不符合JSON标准。错误信息: {e}")
                logger.error(f"原始内容: {applied_content}")

        # 合并所有段落为统一结构
        final_result = {
            "paragraphs": []
        }

        for result in all_results:
            final_result['paragraphs'].extend(result.get('paragraphs', []))
        logger.info(f"样式应用结果：{final_result}")

        dsh.apply_styles_and_save(output_file_path,doc,final_result)

if __name__ == "__main__":

    wp = WordProcessor()
    # wp.check_document_integrity()
    # wp.generate_optimized_document()
    # wp.generate_summary()

    # 调用 generate_word_document_format 方法生成文档
    # wp.generate_word_document_format(file_path, formatted_content)
    user_input_prompt = f"""
        【排版要求】
        - 标题层级 | 字体字号样式对齐
        - 一级标题 | 黑体 16pt 加粗 居中
        - 二级标题 | 楷体_GB2312 14pt 加粗
        - 三级标题 | 楷体_GB2312 14pt 加粗
        - 四级标题 | 楷体_GB2312 12pt 加粗
        - 五级标题 | 楷体_GB2312 12pt 
        - 正文 | 仿宋_GB2312 12pt 
        - 禁用其他字体
        【内容要求】
        不要修改任何内容，添加样式后返回原文。
    """
    file_path = r"C:\Users\cheng\Desktop\产业数智化产品部部门简讯（第19周，2025年05月05日-2025年05月09日）.docx"
    # file_path = r"C:\Users\cheng\Desktop\output_test.docx"
    

    try:
        wp.generate_styled_document(file_path,user_input_prompt)
    except Exception as e:
        print(f"处理文档时发生错误: {e}")